import type { FC, ReactNode } from 'react'
import { memo, useEffect, useRef, useState } from 'react'
import styles from './index.less'
import * as THREE from 'three'
import { pathInfoList } from './util'
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

interface TProps {
  children?: ReactNode
}

const ThreeJsDemo: FC<TProps> = () => {
  const inputRef = useRef<HTMLInputElement>(null)

  const handleToRun = () => {}

  useEffect(() => {
    const scene = new THREE.Scene()
    scene.background = new THREE.Color(0xf0f0ff)
    const geometry = new THREE.BoxGeometry(30, 30, 30)
    // const material = new THREE.MeshBasicMaterial({
    //   color: '#008c8c', //0xff0000设置材质颜色为红色
    //   transparent: true, //开启透明
    //   opacity: 0.5, //设置透明度
    // })
    //MeshLambertMaterial受光照影响
    const material = new THREE.MeshLambertMaterial({
      color: '#008c8c', //0xff0000设置材质颜色为红色
    })
    //点光源：两个参数分别表示光源颜色和光照强度
    // 参数1：0xffffff是纯白光,表示光源颜色
    // 参数2：1.0,表示光照强度，可以根据需要调整
    const pointLight = new THREE.PointLight(0xffffff, 1.5)
    //点光源位置
    pointLight.position.set(500, 500, 200) //点光源放在x轴上
    scene.add(pointLight) //点光源添加到场景中
    const mesh = new THREE.Mesh(geometry, material) //网格模型对象Mesh
    //设置网格模型在三维空间中的位置坐标，默认是坐标原点
    mesh.position.set(0, 10, 0)
    scene.add(mesh)
    const axesHelper = new THREE.AxesHelper(200)
    scene.add(axesHelper)
    //对比三个参数分别对应xyz轴哪个方向
    // new THREE.BoxGeometry(100, 60, 20)

    // 设置模型mesh的xyz坐标
    // mesh.position.set(100, 0, 0)
    // 实例化一个透视投影相机对象
    // 定义相机输出画布的尺寸(单位:像素px)
    const width = 1200 //宽度
    const height = 750 //高度
    // 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000：远裁截面
    const camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 3000)
    //相机在Three.js三维坐标系中的位置
    // 根据需要设置相机位置具体值
    camera.far = 20000
    camera.updateProjectionMatrix()
    camera.position.set(1140, 1138, 1170)
    camera.zoom = 1

    camera.lookAt(mesh.position) //指向mesh对应的位置
    // camera.position.set(-1000, 0, 0)
    // camera.lookAt(0, 0, 0)
    // 相机视线沿着x轴负半轴，mesh位于相机后面，自然看不到
    // camera.position.set(-1000, 0, 0)
    // camera.lookAt(-2000, 0, 0)
    // 创建渲染器对象
    const renderer = new THREE.WebGLRenderer()
    renderer.setSize(width, height) //设置three.js渲染区域的尺寸(像素px)
    renderer.render(scene, camera) //执行渲染操作

    // 设置相机控件轨道控制器OrbitControls
    const controls = new OrbitControls(camera, renderer.domElement)
    controls.target.set(589, 196, 60)
    controls.update()
    // 如果OrbitControls改变了相机参数，重新调用渲染器渲染三维场景
    controls.addEventListener('change', function () {
      renderer.render(scene, camera) //执行渲染操作
    }) //监听鼠标、键盘事件

    // 光源辅助观察
    const pointLightHelper = new THREE.PointLightHelper(pointLight, 10)
    scene.add(pointLightHelper)

    //环境光:没有特定方向，整体改变场景的光照明暗
    const ambient = new THREE.AmbientLight(0xffffff, 0.2)
    scene.add(ambient)

    // 平行光
    // const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
    // 设置光源的方向：通过光源position属性和目标指向对象的position属性计算
    // directionalLight.position.set(50, 200, 150)
    // 方向光指向对象网格模型mesh，可以不设置，默认的位置是0,0,0
    // directionalLight.target = mesh
    // scene.add(directionalLight)

    // DirectionalLightHelper：可视化平行光
    // const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000)
    // scene.add(dirLightHelper)
    // 路径渲染
    // 线条轨道
    let totalLength = 0
    const pathList: any[] = pathInfoList.map((item, index) => {
      const path = new THREE.Path()
      item.path.forEach((info: any) => {
        if ('x' in info) path.lineTo(info.x, info.y)
        else path.quadraticCurveTo(info.x1, info.y1, info.x2, info.y2)
      })
      const { position, length, plane } = item
      totalLength += length
      return {
        path: path,
        position,
        plane,
        length,
        key: index,
        totalLength,
      }
    })

    // 线条加入场景
    pathList.forEach((item, i) => {
      // if (i > 2) return
      const points = item.path.getPoints()
      const lineGeometry = new THREE.BufferGeometry().setFromPoints(points)
      const lineMaterial = new THREE.LineBasicMaterial({
        color: 'red',
        // transparent: true,
        opacity: 1,
      })
      const line = new THREE.Line(lineGeometry, lineMaterial)
      const { x, y, z } = item.position
      line.position.set(x, y, z)
      switch (item.plane) {
        case 'xy':
        case '-x-y':
        case 'x-y':
          break
        case '-z-x':
          line.rotateX((90 * Math.PI) / 180)
          break
        case '-zx':
          line.rotateZ((-90 * Math.PI) / 180)
          line.rotateY((-90 * Math.PI) / 180)
          break

        default:
          break
      }
      scene.add(line)
    })

    // 渲染循环
    // const clock = new THREE.Clock()
    let direction = 1
    let inputDistance = 0,
      targeDistance = 0,
      speed = 10
    function renderRobot() {
      // 处理过大过小的输入距离
      if (inputDistance > pathList[pathList.length - 1].totalLength)
        inputDistance = pathList[pathList.length - 1].totalLength
      else if (inputDistance < 0) {
        inputDistance = 0
      }

      // 处理方向
      if (targeDistance < inputDistance) direction = 1
      else if (targeDistance > inputDistance) direction = -1
      // 循环运动
      inputDistance += 3 * direction
      // 步进
      if (Math.abs(targeDistance - inputDistance) <= speed) targeDistance = inputDistance
      else targeDistance += speed * direction
      // 找到位置所在的line
      const targetPath = pathList.find((item) => item.totalLength > targeDistance)

      // 若不存在直接结束
      if (!targetPath) {
        renderer.render(scene, camera)
        return
      }
      // 比例换算目标在1之间的距离
      const proportion = 1 / targetPath.length
      let targetProgress = proportion * (targeDistance - targetPath.totalLength + targetPath.length)
      // 处理边际问题
      if (targetProgress <= 0) targetProgress = 0.001
      else if (targetProgress >= 1) targetProgress = 9.999

      // 在线上获取位置和朝向
      const point = targetPath.path.getPointAt(targetProgress)
      let lookPoint = targetPath.path.getPointAt(targetProgress + 0.001 * direction)

      // 前方无点朝向后方
      if (!lookPoint) lookPoint = targetPath.path.getPointAt(targetProgress - 0.001 * direction)

      // 计算位置以及朝向
      let robotPosition: THREE.Vector3
      let robotLookPosition: THREE.Vector3
      const { x, y, z } = targetPath.position
      switch (targetPath.plane) {
        case 'xy':
        case '-x-y':
        case 'x-y':
          robotPosition = new THREE.Vector3(point.x + x, point.y + y, 0 + z)
          robotLookPosition = new THREE.Vector3(lookPoint.x + x, lookPoint.y + y, 0 + z)
          break
        case '-zx':
          robotPosition = new THREE.Vector3(point.y + x, 0 + y, point.x + z)
          robotLookPosition = new THREE.Vector3(lookPoint.y + x, 0 + y, lookPoint.x + z)
          break
        case '-z-x':
          robotPosition = new THREE.Vector3(point.x + x, 0 + y, point.y + z)
          robotLookPosition = new THREE.Vector3(lookPoint.x + x, 0 + y, lookPoint.y + z)
          break
        default:
          robotPosition = new THREE.Vector3(point.x, point.y, 0)
          robotLookPosition = new THREE.Vector3(lookPoint.x, lookPoint.y, 0)
          break
      }
      mesh.position.copy(robotPosition)
      mesh.lookAt(robotLookPosition)

      renderer.render(scene, camera)
    }
    renderer.setAnimationLoop(renderRobot)

    function render() {
      //   const spt = clock.getDelta() * 1000 //毫秒
      //   console.log('两帧渲染时间间隔(毫秒)', spt)
      //   console.log('帧率FPS', 1000 / spt)

      mesh.rotateY(0.01) //每次绕y轴旋转0.01弧度
      if (mesh.position.x >= 150) {
        direction = -1
      }
      if (mesh.position.x <= 0) {
        direction = 1
      }

      mesh.position.x += 0.5 * direction
      mesh.position.y += 0.5 * direction
      mesh.position.z += 0.5 * direction
      //   mesh.rotateX(0.01)
      //   mesh.rotateZ(0.01)
      renderer.render(scene, camera) //执行渲染操作
      requestAnimationFrame(render) //请求再次执行渲染函数render，渲染下一帧
    }
    // render()

    document.getElementById('webgl')?.appendChild(renderer.domElement)
  }, [])
  return (
    <div
      className={styles.threeJsDemo}
      id="threeJsDemo"
    >
      <h2>测试下threeJS</h2>
      <input
        type="text"
        ref={inputRef}
      />
      <button
        onClick={handleToRun}
        type="button"
      >
        运动起来
      </button>
      <div
        id="webgl"
        style={{ margin: '30px' }}
      ></div>
    </div>
  )
}

export default memo(ThreeJsDemo)
